﻿using Bridge.Shared.Enums.IdentityService;
using Bridge.Shared.Extensions;
using Bridge.Shared.Models;
using Bridge.System.Domain;
using FileManagement;
using Mapster;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using NPOI.HPSF;
using SysConfigs;
using SysConfigs.Output;
using SysFiles;
using SysFiles.Input;
using SysFiles.Output;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web.Services.Description;
using Volo.Abp.Application.Services;
using Volo.Abp.BlobStoring;
using Volo.Abp.BlobStoring.Minio;
using Volo.Abp.Domain.Repositories;

namespace Bridge.System.Application
{
    public class SysFileService : ApplicationService, ISysFileService
    {
        private IRepository<SysFile, Guid> _sysFileRepository;
        private SysOptions _sysOptions;
        private IWebHostEnvironment WebHostEnvironment;
        // 注入类型化Blob容器接口
        private readonly IBlobContainer<FilesContainer> _blobContainer;
        private readonly IConfiguration _cfg;
        public SysFileService(IRepository<SysFile, Guid> sysFileRepository,
            IOptions<SysOptions> sysOptions,
            IWebHostEnvironment webHostEnvironment,
            IBlobContainer<FilesContainer> blobContainer,
            IConfiguration cfg)
        {

            _sysFileRepository = sysFileRepository;
            _sysOptions = sysOptions.Value;
            WebHostEnvironment = webHostEnvironment;
            _blobContainer = blobContainer;
            _cfg = cfg;
        }

        public Task<ResponseResult> DeleteFileByUrl(IdsInput<string> urls)
        {
            throw new NotImplementedException();
        }

        public Task<ResponseResult> DownloadFile(string path, long fileId = 0)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 获取文件列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PageResponseResult<SysFileOutput>> GetSysFilesPageList(SysFilePageInput input)
        {
            var query = (await _sysFileRepository.GetQueryableAsync())
                        .WhereIf(!string.IsNullOrEmpty(input.FileName), a => a.FileName.Contains(input.FileName))
                        .WhereIf(input.StoreType.HasValue, a => a.StoreType == input.StoreType)
                        .WhereIf(input.UploadStartTime.HasValue, a => a.CreationTime >= input.UploadStartTime.Value)
                        .WhereIf(input.UploadEndTime.HasValue,a=>a.CreationTime<=input.UploadEndTime);

            var totalCount = query.Count(); ;
            var list = await query.Skip(input.PageSize * input.PageIndex).Take(input.PageSize).ToListAsync();
            var dto = list.Adapt<List<SysFileOutput>>();

            return PageResponseResult<SysFileOutput>.Success("ok", dto, input.PageSize, input.PageIndex, totalCount);
        }

        public async Task<ResponseResult<SysFileOutput>> UploadFile(UploadFileInput input)
        {
            //校验文件大小、类型
            if(input == null || input.file == null)
            {
                return ResponseResult<SysFileOutput>.Fail("上传文件不能为空");
            }
            SysFile sysfile = new();
            IFormFile formFile = input.file;
            string fileExt = Path.GetExtension(formFile.FileName);//文件后缀
            double fileSize = Math.Round(formFile.Length / 1024.0, 2);//文件大小KB
            if (_sysOptions.UploadFile.NotAllowedExt.Contains(fileExt))
            {
                return ResponseResult<SysFileOutput>.Fail("上传失败，未经允许上传类型！");
            }
            if (fileSize > _sysOptions.UploadFile.MaxSize) {
                return ResponseResult<SysFileOutput>.Fail("上传失败，文件大小超出限制！");
            }

            //根据配置存储类型上传文件到不同的地方
            StoreTypeEnum storeType = (StoreTypeEnum)_sysOptions.UploadFile.StoreType;
            switch(storeType)
            {
                case StoreTypeEnum.LOCAL:
                    string savePath = Path.Combine(WebHostEnvironment.WebRootPath);
                    if (input.FileDir.IsEmpty())
                    {
                        input.FileDir = _sysOptions.UploadFile.LocalSavePath;
                    }
                    sysfile = await SaveFileToLocal(savePath, input.FileName, input.FileDir, formFile);
                    break;
                case StoreTypeEnum.Minio:
                    sysfile = await SaveFileToMinio(input.FileName, input.FileDir, formFile);
                    break;
                case StoreTypeEnum.ALIYUN:
                    break;
            }


            return ResponseResult<SysFileOutput>.Success("ok",new SysFileOutput() {
                FileUrl = sysfile.AccessUrl,
                FileName = sysfile.FileName,
                Id = sysfile.Id
            });
        }
        protected string FormatPathName(string path)
        {
            path = $"/{path}/".Trim("./".ToArray());
            if (path.Length == 0) return "/";
            return $"/{path}/";
        }
        private async Task<SysFile> SaveFileToMinio(string fileName,string fileDir,IFormFile formFile)
        {
            
                string fileExt = Path.GetExtension(formFile.FileName);
                var guid = Guid.NewGuid();
                fileName = (fileName.IsEmpty() ? $"{guid}_{formFile.FileName}" : fileName + fileExt);

                string filePath = $"/{DateTime.Now:yyyy}/{DateTime.Now:MMdd}";
            // string fileFullPath = Path.Combine(filePath, fileName);
            string fileFullPath = string.Concat(filePath.Replace("\\", "/"), "/", fileName);
                var fileByte = await formFile.GetAllBytesAsync();
                await _blobContainer.SaveAsync(fileFullPath, fileByte,true).ConfigureAwait(false);

                var minioUrl = $"/{BlobContainerNameAttribute.GetContainerName<FilesContainer>()}/{fileFullPath}";
                double fileSize = Math.Round(formFile.Length / 1024.0, 2);

                Uri baseUri = new(fileFullPath);
                Uri fullUrl = new(baseUri, minioUrl);
                SysFile file = new SysFile(GuidGenerator.Create())
                {
                    RealName = formFile.FileName,
                    FileName = fileName,
                    FileExt = fileExt,
                    FileSize = fileSize + "kb",
                    StoreType = (int)StoreTypeEnum.LOCAL,
                    StorePath = fileDir,
                    FileType = formFile.ContentType,
                    FileUrl = minioUrl,
                    AccessUrl = fullUrl.AbsoluteUri,

                };
                await _sysFileRepository.InsertAsync(file);
                return file;
            
           
        }
        /// <summary>
        /// 存储本地
        /// </summary>
        /// <param name="fileDir">存储文件夹</param>
        /// <param name="rootPath">存储根目录</param>
        /// <param name="fileName">自定文件名</param>
        /// <param name="formFile">上传的文件流</param>
        /// <param name="userName"></param>
        /// <returns></returns>
        public async Task<SysFile> SaveFileToLocal(string rootPath, string fileName, string fileDir, IFormFile formFile)
        {
            string fileExt = Path.GetExtension(formFile.FileName);
            fileName = (fileName.IsEmpty() ? HashFileName() : fileName) + fileExt;

            string filePath = GetdirPath(fileDir);
            string finalFilePath = Path.Combine(rootPath, filePath, fileName);
            double fileSize = Math.Round(formFile.Length / 1024.0, 2);

            if (!Directory.Exists(Path.GetDirectoryName(finalFilePath)))
            {
                Directory.CreateDirectory(Path.GetDirectoryName(finalFilePath));
            }

            using (var stream = new FileStream(finalFilePath, FileMode.Create))
            {
                await formFile.CopyToAsync(stream);
            }
            string uploadUrl = _sysOptions.UploadFile.UploadUrl;
            string accessPath = string.Concat(filePath.Replace("\\", "/"), "/", fileName);
            Uri baseUri = new(uploadUrl);
            Uri fullUrl = new(baseUri, accessPath);
            SysFile file = new SysFile(GuidGenerator.Create())
            {
                RealName = formFile.FileName,
                FileName = fileName,
                FileExt = fileExt,
                FileSize = fileSize + "kb",
                StoreType = (int)StoreTypeEnum.LOCAL,
                StorePath=fileDir,
                FileType = formFile.ContentType,
                FileUrl = finalFilePath.Replace("\\", "/"),
                AccessUrl = fullUrl.AbsoluteUri,

            };
           await _sysFileRepository.InsertAsync(file);
            return file;
        }

        public string HashFileName(string str = null)
        {
            if (string.IsNullOrEmpty(str))
            {
                str = Guid.NewGuid().ToString().ToLower();
            }
            return BitConverter.ToString(MD5.HashData(Encoding.Default.GetBytes(str)), 4, 8).Replace("-", "");
        }

        /// <summary>
        /// 获取文件存储目录
        /// </summary>
        /// <param name="storePath"></param>
        /// <param name="byTimeStore">是否按年月日存储</param>
        /// <returns></returns>
        public string GetdirPath(string storePath = "", bool byTimeStore = true)
        {
            DateTime date = DateTime.Now;
            string timeDir = date.ToString("yyyy/MMdd");

            if (!string.IsNullOrEmpty(storePath))
            {
                timeDir = Path.Combine(storePath, timeDir);
            }
            return timeDir;
        }

        public async Task<ResponseResult> DeleteFileById(IdsInput<Guid> input)
        {
            if (input == null || !input.Ids.Any())
            {
                return ResponseResult.Fail("请求参数不能为空！");
            }

            var files =await _sysFileRepository.GetListAsync(a => input.Ids.Contains(a.Id));
            //删本地文件
            foreach (var item in files)
            {
                if (File.Exists(item.FileUrl))
                {
                    File.Delete(item.FileUrl);
                }
                
            }
            //删数据库文件
            await _sysFileRepository.DeleteManyAsync(files);

            return ResponseResult.Success();
        }
    }
}
